package com.karina.visualization.chart.component;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.user.client.Timer;
import com.karina.visualization.chart.client.DisplayObject;
import com.karina.visualization.chart.client.Series;
import com.karina.visualization.chart.model.ChartModel;
import com.karina.visualization.chart.model.SeriesModel;
import com.karina.visualization.chart.skins.ChartPaneSkin;

abstract public class ChartBase<Model extends ChartModel<DataType>, DataType>
		extends ChartObject<Model, ChartPaneSkin<Model>> {

	protected ChartBase(Model model, ChartPaneSkin<Model> skin) {
		super(model, skin);
	}

	private final List<Series> seriesList = new ArrayList<Series>();

	private final UpdateManager updateManager = new UpdateManager();

	private final Timer effecttimer = new Timer() {

		@Override
		public void run() {
			if (!next()) {
				effecttimer.cancel();
			}
		}
	};;

	@Override
	protected UpdateManager getUpdateManager() {
		return updateManager;
	}

	@SuppressWarnings("rawtypes")
	@Override
	protected void caculateChartObjectProperties() {
		// on based class just invalidate the property to
		// make the component redraw itsself
		int measuredWidth = getLeft() + getRight() + getData().getWidth();
		getContext().setWidth(measuredWidth);
		getData().setX(getLeft());

		int measuredHeight = getTop() + getBottom() + getData().getHeight();
		getContext().setHeight(measuredHeight);
		getData().setY(getTop());

		List<DisplayObject<?>> children = getChildren();

		for (DisplayObject child : children) {
			if (child instanceof ChartObject) {
				setChildObjectProperties((ChartObject) child);
			}
		}

	}

	@Override
	public void play(int duration) {
		// invalidateModelProperties = true;
		super.play(duration);

		List<Series> serieses = getSeries();
		for (Series se : serieses) {
			if (se instanceof ChartObject) {
				((ChartObject<?, ?>) se).enableEffect(enableEffect);
				((ChartObject<?, ?>) se).play(duration);
			}
		}
		effecttimer.cancel();
		effecttimer.scheduleRepeating(FRAME_DURATION);
	}

	@Override
	protected boolean next() {
		boolean ret = true;
		invalidateModelProperties = true;
		super.next();

		List<Series> serieses = getSeries();
		for (Series se : serieses) {
			if (se instanceof ChartObject) {
				if (!((ChartObject<?, ?>) se).next()) {
					ret = false;
				}
			}
		}
		return ret;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected void setSeriesMargin(int marginLeft, int marginBottom) {
		for (Series series : seriesList) {
			((UpdateableObject<SeriesModel>) series).getData().setMarginLeft(
					marginLeft);
			((UpdateableObject<SeriesModel>) series).getData().setMarginBottom(
					marginBottom);
		}
	}

	protected void setTextMargin(int marginLeft, int marginBottom) {
		getData().setMarginLeft(marginLeft);
		getData().setMarginBottom(marginBottom);
	}

	protected List<Series> getSeries() {
		return seriesList;
	}

	@SuppressWarnings({ "rawtypes" })
	void caculateSeries() {
		getData().caculatSeries();

		List<SeriesModel> serieses = getData().getSerieses();

		serieses = filterSeries(serieses);

		int i = 0;
		for (SeriesModel<DataType> s : serieses) {
			s.setIndex(i);
			i++;
			Series se = createSeries(s);
			seriesList.add(se);
			addChild(se);
		}
	}

	@SuppressWarnings("rawtypes")
	protected List<SeriesModel> filterSeries(List<SeriesModel> newSeries) {
		List<SeriesModel> unprocssesedSeries = new ArrayList<SeriesModel>(
				newSeries);
		List<Series> retainedSeries = new ArrayList<Series>();
		if (seriesList != null) {
			for (Series<SeriesModel<DataType>> s : seriesList) {
				boolean founded = false;
				for (SeriesModel nm : unprocssesedSeries) {
					if (s.getData().getName().equals(nm.getName())) {
						founded = true;
						nm.setIndex(s.getData().getIndex());
						s.setData(nm);
						retainedSeries.add(s);
						unprocssesedSeries.remove(nm);
						break;
					}
				}
				if (!founded)
					removeChild(s);
			}

		}

		seriesList.retainAll(retainedSeries);

		return unprocssesedSeries;
	}

	abstract protected Series createSeries(SeriesModel model);

	@SuppressWarnings("rawtypes")
	@Override
	public void addChild(DisplayObject child) {

		if (child instanceof ChartObject) {
			setChildObjectProperties((ChartObject) child);
		}
		super.addChild(child);
	}

	@SuppressWarnings("rawtypes")
	protected void setChildObjectProperties(ChartObject obj) {
		if (obj.getData() != null) {
			obj.getData().setLeft(this.getLeft());
			obj.getData().setTop(this.getTop());
			obj.getData().setRight(this.getRight());
			obj.getData().setBottom(this.getBottom());
			obj.getData().setX(getData().getX());
			obj.getData().setY(getData().getY());
		}
	}

	@Override
	protected void caculateChartObjectModelProperties() {
		// on based class redraw the component
		// invalidteChartObjectModel();
	}

	public void setLeft(int left) {
		getData().setLeft(left);
	}

	public int getLeft() {
		return getData().getLeft();
	}

	public void setTop(int top) {
		getData().setTop(top);
	}

	public int getTop() {
		return getData().getTop();
	}

	public void setRight(int right) {
		getData().setRight(right);
	}

	public int getRight() {
		return getData().getRight();
	}

	public void setBottom(int bottom) {
		getData().setBottom(bottom);
	}

	public int getBottom() {
		return getData().getBottom();
	}

}
